package com.zhkc.wisdom.common.redis.agent;

import com.alibaba.fastjson2.JSONObject;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RedisAgent {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ListOperations<String, Object> redisListOperations;

    /**
     * 功能：添加队列对象，到队列尾部
     *
     * @param key
     * @param str
     * @return 栈的长度
     */
    public Long addQueue(String key, String str) {
        return redisListOperations.rightPush(key, str);
    }

    /**
     * 功能：批量添加队列对象到队列尾部
     *
     * @param key
     * @param strs
     * @return 栈的长度
     */
    public Long addQueueAll(String key, List<String> strs) {
        return redisListOperations.rightPushAll(key, strs);
    }

    /**
     * 添加对象 到队列头部
     *
     * @return 栈的长度
     */
    // public Long addQueueFirst(String key, BaseEntity object) {
    // 	return redisListOperations.leftPush(key, object.toString());
    // }

    /**
     * 功能：
     *
     * @param key
     * @param str
     * @return 栈的长度
     */
    public Long addQueueFirst(String key, String str) {
        return redisListOperations.leftPush(key, str);
    }

    /**
     * 弹出队列对象，默认弹出队列头部
     */
    public Object popQueueObject(String key) {
        return redisListOperations.leftPop(key);
    }

    /**
     * 弹出队列对象，默认弹出队列头部
     */
    public String popQueueString(String key) {
        Object o = redisListOperations.leftPop(key);
        if (o != null) {
            return o.toString();
        }
        return "";
    }

    /**
     * 弹出队列对象，弹出队列尾部对象
     */
    public Object popQueueEndObject(String key) {
        return (String) redisListOperations.rightPop(key);
    }

    /**
     * 得到一个队列数据，不移除队列
     */
    public List<Object> getQueueObjects(String key, int startIndex, int endIndex) {
        return redisListOperations.range(key, startIndex, endIndex);
    }

    /**
     * 得到一个队列的长度
     */
    public long getQueueLength(String key) {
        return redisListOperations.size(key);
    }

    /**
     * 功能：检索
     *
     * @param key
     * @param index
     * @return
     */
    public String indexQueue(String key, long index) {
        return (String) redisListOperations.index(key, index);
    }

    /**
     * 功能：裁剪 队列,裁剪后剩余裁剪范围内的队列
     *
     * @param key
     * @param start
     * @param end
     */
    public void trimQueue(String key, long start, int end) {
        redisListOperations.trim(key, start, end);
    }

    /**
     * 新增String,如果key不存在则增加，返回true，key存在，返回false,不操作
     *
     * @param key     键
     * @param value   值
     * @param timeout 超时(秒)
     * @return true 操作成功，false 已存在值
     */
    public boolean addString(String key, String value, Long timeout) {
        return addString(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置k-v对，并设置有效期
     *
     * @param key
     * @param value
     * @param expire   k-v对有效期
     * @param timeUnit 有效期单位
     * @return true表示key存在；false表示不操作
     */
    public boolean addString(String key, String value, Long timeout, TimeUnit timeUnit) {
        return Boolean.TRUE.equals(redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            Boolean result = null;
            if (timeout != null && timeout > 0) {
                result = connection.set(key.getBytes(), value.getBytes(), Expiration.from(timeout, timeUnit),
                        SetOption.SET_IF_ABSENT);
            } else {
                result = connection.setNX(key.getBytes(), value.getBytes());
            }
            if (null != result) {
                return result;
            }
            return false;
        }));
    }

    /**
     * 功能：添加字段串，如果key存在，则不修改内容，但延长有效时间
     *
     * @param key     键
     * @param value   值
     * @param timeout 超时(秒)
     * @returntrue 操作成功表示添加字段成功，或者延时成功，false操作失败
     */
    public boolean addStringOrSetExpire(String key, String value, Long timeout) {
        return Boolean.TRUE.equals(redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.set(key.getBytes(), value.getBytes());
            if (timeout != null && timeout > 0) {
                connection.expire(key.getBytes(), timeout);
            }
            return true;
        }));
    }

    /**
     * 功能：设置有效时间 如果key不存在 返回false，key存在，操作成功返回true
     *
     * @param key
     * @param timeout 秒
     * @return
     */
    public boolean setExpire(String key, Long timeout) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            boolean result1 = connection.exists(key.getBytes());
            if (result1 && timeout != null && timeout > 0) {
                connection.expire(key.getBytes(), timeout);
                return true;
            }
            return false;
        });
    }

    /**
     * 功能：获取有效期剩余时间
     *
     * @param key
     * @return 秒
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    public String getString(String key) {
        return redisTemplate.execute((RedisCallback<String>) connection -> {
            byte[] result = connection.get(key.getBytes());
            if (result != null && result.length > 0) {
                return new String(result);
            }
            return null;
        });
    }

    /**
     * 功能：设置字符串 覆盖之前的数据
     *
     * @param key
     * @param value
     * @param timeout 超时时间 秒
     * @return
     */
    public boolean setString(String key, String value, Integer timeout) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.set(key.getBytes(), value.getBytes());
            if (timeout != null && timeout > 0) {
                return connection.expire(key.getBytes(), timeout);
            }
            return true;
        });
    }

    public boolean setStringNx(String key, String value) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.setNX(key.getBytes(), value.getBytes());
        });
    }

    /**
     * 功能：删除线上的
     *
     * @param key
     */
    public void deleteString(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 功能：新加 序列化对象
     *
     * @param key
     * @param object
     * @param clazz
     * @return
     */
    public boolean setBytes(String key, byte[] bytes, Integer timeOutSeconds) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            Boolean result = connection.set(key.getBytes(), bytes, Expiration.from(timeOutSeconds, TimeUnit.SECONDS),
                    SetOption.SET_IF_ABSENT);
            if (null != result) {
                return result;
            }
            return false;
        });
    }

    public byte[] getBytes(String key) {
        return redisTemplate.execute((RedisCallback<byte[]>) connection -> {
            byte[] result = connection.get(key.getBytes());
            if (result != null && result.length > 0) {
                return result;
            }
            return null;
        });
    }

    /**
     * 功能：新加 序列化对象
     *
     * @param key
     * @param object
     * @param clazz
     * @return
     */
    public boolean setObject(String key, Object object, Class<?> clazz) {
        redisTemplate.opsForValue().set(key, clazz.cast(object));
        return true;
    }

    /**
     * 功能：如果不存在则设置值
     *
     * @param key
     * @param object
     * @return
     */
    public Boolean setIfAbsent(String key, Object object) {
        return redisTemplate.opsForValue().setIfAbsent(key, object);
    }

    /**
     * 功能：新加 序列化对象
     *
     * @param key     键
     * @param object  值
     * @param timeout 超时时间，秒
     * @return 是否保存成功或刷新成功
     */
    public boolean setObject(String key, Object object, Integer timeout, Class<?> clazz) {
        redisTemplate.opsForValue().set(key, clazz.cast(object), timeout, TimeUnit.SECONDS);
        return true;
    }

    /**
     * 功能：调置值
     *
     * @param key
     * @param object
     * @param timeout 超时时间 秒
     */
    public boolean setObject(String key, Object object, Integer timeoutSecond) {
        if (timeoutSecond > 0) {
            redisTemplate.opsForValue().set(key, object, timeoutSecond, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().set(key, object);
        }
        return true;
    }

    /**
     * 功能：获取对象
     *
     * @param key
     * @return
     */
    public Object getObject(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public <T> T getObject(String key, Class<T> clazz) {
        String strCache = getString(key);
        if (StringUtils.isNotBlank(strCache)) {
            return JSONObject.parseObject(strCache, clazz);
        }
        return null;
    }

    /**
     * 功能：删除实体
     *
     * @param key
     * @return
     */
    public Object removeObject(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 功能：设置保存一个实体对象，方法内部拼前缀frame:entity:
     *
     * @param key
     * @param object
     * @param timeout
     * @param clazz
     * @return
     */
    public <T> boolean addEntity(String preKey, T object, Integer timeout) {
        if (preKey == null || object == null) {
            return false;
        }
        String redisKey = preKey + ":" + object.getClass().getSimpleName();
        return setObject(redisKey, object, timeout != null ? timeout : 0);
    }

    /**
     * 功能：取回实体对象
     *
     * @param key
     * @param clazz
     * @return
     */
    public <T> T getEntity(String preKey, Class<T> clazz) {
        String redisKey = preKey + ":" + clazz.getSimpleName();
        String strCache = getString(redisKey);
        if (StringUtils.isNotBlank(strCache)) {
            return JSONObject.parseObject(strCache, clazz);
        }
        return null;
    }

    /**
     * 功能：取回实体对象 ,并刷新有效时间
     *
     * @param preKey
     * @param clazz
     * @param timeout
     * @return
     */
    public <T> T getEntityAndRefreshTimeout(String preKey, Class<T> clazz, Integer timeout) {
        String redisKey = preKey + ":" + clazz.getSimpleName();
        String strCache = getString(redisKey);
        if (StringUtils.isNotBlank(strCache)) {
            setExpire(redisKey, timeout != null ? (long) timeout : 0);
            return JSONObject.parseObject(strCache, clazz);
        }
        return null;
    }

    /**
     * 功能：删除实体对象
     *
     * @param preKey
     * @return
     */
    public <T> void removeEntity(String preKey, Class<T> clazz) {
        String redisKey = preKey + ":" + clazz.getSimpleName();
        deleteString(redisKey);
    }

    /**
     * 功能：获取实体对象的全部id
     *
     * @param clazz
     * @return
     */
    public <T> Set<String> getEntityKeys(String preKey, Class<T> clazz) {
        String redisKey = preKey + ":" + clazz.getSimpleName() + "*";
        return redisTemplate.keys(redisKey);
    }

    /**
     * 功能：获取某个匹配值的全部key值
     *
     * @param pattern
     * @return
     */
    public Set<String> getKeys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 功能：对某个key的数据增加指定变量的数值
     *
     * @param key
     * @param delta 变量值
     * @return
     */
    public Boolean increment(String key, Long delta) {
        return redisTemplate.opsForValue().increment(key, delta) > 0;
    }

    public Long incrementReturnLong(String key, Long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 功能：key是否存在
     *
     * @param key
     * @return
     */
    public Boolean exists(String key) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.exists(key.getBytes()));
    }

    /**
     * 设置 list
     *
     * @param <T>
     * @param key
     */
    public <T> void setList(String key, List<T> list, Integer timeoutSecond) {
        setObject(key, list, timeoutSecond);
    }


}
